package it.unipd.dei.es1011.nucularc0d3.defaultproject.util;


import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Vector;

import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import org.xmlpull.v1.XmlSerializer;

import android.content.Context;
import android.util.Log;
import android.util.Xml;
/**
 * This class have the responsibility of write and retrieve data to and from
 * the storage.
 * @author darkfrank
 *
 */
public class DataWorker {	
	final static String thumbName = "thumb.xml";
	
	/**
	 * Generate the name of the xml that store the session corresponding
	 * to the passed Details. 
	 * 
	 * @param details the details of the session recorded
	 * @return the string represent the fullPathName
	 */
	public static String generateFullPathName(Details details){
		//generate filename
		return "session_" + details.date.replace("/", "") + "-" + 
										 details.time.replace(":", "") + ".xml";
	}
	
	/**
	 * Write a single recorded session on disk in XML.
	 * 
	 * @param fileName
	 *            String that represents the name of the file
	 * @param details
	 *            Details carry information about the session {date, time,
	 *            axis}
	 * @param samples
	 *            an Iterator<Float>[] with the steps of quantization for X, Y, Z
	 * @param context
	 *            the Context of application
	 * @throws FileNotFoundException
	 *             in case the file can't be created.
	 */
	public static void writeSessionOnDisk(Details details, Vector<Iterator<Float>> samples, 
			Context context) throws FileNotFoundException {
		/*
		 * <?xml version='1.0' encoding='UTF-8' standalone='yes' ?>
		 * <session date="21/05/11" time="15:18" duration="3600" sample_rate="1.3" >
		 * 	<axis name="X">
		 * 		<sample>0.2</sample>
		 * 		<sample>...</sample>
		 * 		<sample>0.1</sample>
		 * 	</axis>
		 * 	<axis name="Y">
		 * 		<sample>0.2</sample>
		 * 		<sample>...</sample>
		 * 		<sample>0.1</sample>
		 * 	</axis>
		 * </session>
		 */

		// retrieve filename
		String fileName = generateFullPathName(details);
		
		//create a new FileOutputStream into internal memory
		FileOutputStream fileos = context.openFileOutput(fileName, Context.MODE_PRIVATE);

		//we create a XmlSerializer in order to write xml data
		XmlSerializer serializer = Xml.newSerializer();
		try {
			//we set the FileOutputStream as output for the serializer, using UTF-8 encoding
			serializer.setOutput(fileos, "UTF-8");
			//Write <?xml declaration with encoding (if encoding not null) and standalone flag (if standalone not null)
			serializer.startDocument(null, Boolean.valueOf(true));
			//set indentation option
			serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);

			//start a tag called "session"
			serializer.startTag(null, "session");

			//set attributes called "date", "time", "duration" and "sample_rate" 
			serializer.attribute(null, "date", details.date);
			serializer.attribute(null, "time", details.time);
			serializer.attribute(null, "duration", details.duration);
			serializer.attribute(null, "sample_rate", details.sample_rate);

			int index = 0;
			if(details.x){
				//start a tag called "axis"
				serializer.startTag(null, "axis");

				//set an attribute called "name"
				serializer.attribute(null, "name", "X");

				while(samples.get(index).hasNext()){
					//start a tag called "sample"
					serializer.startTag(null, "sample");
					//write some text inside <sample>
					serializer.text(String.valueOf(samples.get(index).next()));
					//close a tag called "sample"
					serializer.endTag(null, "sample");
				}
				//close a tag called "axis"
				serializer.endTag(null, "axis");

				index ++;
			}

			if(details.y){
				//start a tag called "axis"
				serializer.startTag(null, "axis");

				//set an attribute called "name"
				serializer.attribute(null, "name", "Y");

				while(samples.get(index).hasNext()){
					//start a tag called "sample"
					serializer.startTag(null, "sample");
					//write some text inside <sample>
					serializer.text(String.valueOf(samples.get(index).next()));
					//close a tag called "sample"
					serializer.endTag(null, "sample");
				}
				//close a tag called "axis"
				serializer.endTag(null, "axis");

				index ++;
			}

			if(details.z){
				//start a tag called "axis"
				serializer.startTag(null, "axis");

				//set an attribute called "name"
				serializer.attribute(null, "name", "Z");

				while(samples.get(index).hasNext()){
					//start a tag called "sample"
					serializer.startTag(null, "sample");
					//write some text inside <sample>
					serializer.text(String.valueOf(samples.get(index).next()));
					//close a tag called "sample"
					serializer.endTag(null, "sample");
				}
				//close a tag called "axis"
				serializer.endTag(null, "axis");
			}

			//close a tag called "session"
			serializer.endTag(null, "session");
			serializer.endDocument();
			//write xml data into the FileOutputStream
			serializer.flush();
			//finally we close the file stream
			fileos.close();	
			
			// update thumb on the disk
			List<Details> sessions = new LinkedList<Details>();
			DataWorker.loadThumbsFromDisk(sessions, context);
			sessions.add(0, details);
			DataWorker.writeThumbOnDisk(sessions.iterator(), context);
			// end update
		} catch (Exception e) {
			Log.e("Exception","error occurred while creating xml file");
			e.printStackTrace();
		}
	}

	/**
	 * 
	 * Load a single recorded session from the disk in XML.
	 * @param fileName 
	 * 
	 * @param fileName
	 *            String that represents the name of the file, call generateFullPathName 
	 *            to obtain a formatted one.
	 * @param details
	 *            Details the information about the session {date, time, axis}
	 *            are returned here
	 * @param samples
	 *            Vector<List<Float>> the steps of quantization are returned here
	 * @param context
	 *            the Context of application
	 */
	public static void loadSessionFromDisk(String fileName, final Details details, final Vector<List<String>> samples, Context 
			context){
		/*
		 * <?xml version='1.0' encoding='UTF-8' standalone='yes' ?>
		 * <session>
		 * 	<axis name="X">
		 * 		<sample>0.2</sample>
		 * 		<sample>...</sample>
		 * 		<sample>0.1</sample>
		 * 	</axis>
		 * 	<axis name="Y">
		 * 		<sample>0.2</sample>
		 * 		<sample>...</sample>
		 * 		<sample>0.1</sample>
		 * 	</axis>
		 * </session>
		 */
		
		try{
			//create a new instance of SAXParser from the Factory
			SAXParserFactory factory = SAXParserFactory.newInstance();
			SAXParser saxParser = factory.newSAXParser();

			//create a new handler to bring the data out of the XML file
			DefaultHandler handler = new DefaultHandler() {

				boolean session = false;
				boolean sample = false;
				boolean axis = false;
				int index = 0;
			
				// when a start element are found
				public void startElement(String uri, String localName, String qName, 
						Attributes attributes) throws SAXException {
					//if "session" we have to retrieve the attributes inside of it
					if (qName.equalsIgnoreCase("session")) {
						
						// Get values of each attribute
						details.date = attributes.getValue(0);
						details.time = attributes.getValue(1);
						details.duration = attributes.getValue(2);
						details.sample_rate = attributes.getValue(3);
						
						session = true;
					}
					//if "sample" nothing to do
					if (qName.equalsIgnoreCase("sample")) {
						sample = true;
					}
					// if "axis" we have to retrieve the name of it
					if (qName.equalsIgnoreCase("axis")){
						if(attributes.getValue(0).equalsIgnoreCase("X")){
							details.x = true;
							samples.add(new ArrayList<String>());
						}
						else if(attributes.getValue(0).equalsIgnoreCase("Y")){
							details.y = true;
							samples.add(new ArrayList<String>());
						}
						else {
							details.z = true;
							samples.add(new ArrayList<String>());
						}
						axis = true;
					}
				}

				// in case of end element
				public void endElement(String uri, String localName,
						String qName) throws SAXException {
					// if "axis" increment the index
					if (qName.equalsIgnoreCase("axis")){
						index ++;
					}
				}

				// in case of character inside tag 
				public void characters(char ch[], int start, int length)
				throws SAXException {

					if (session) {
						session = false;
					}

					// in "sample" case we have to store value
					if (sample) {				
						samples.get(index).add(new String(ch, start, length));
						sample = false;
					}

					if (axis) {
						axis = false;
					}
				}
			};

			//create a new FileInputStream into internal memory
			FileInputStream fileis = context.openFileInput(fileName);
			// parse xml file.
			saxParser.parse(fileis, handler);
		} catch (Exception e) {
			/* Display any Error to the GUI. */
			Log.e("Debug", "WeatherQueryError", e);
		}
	}

	/**
	 * Delete the corresponding session file, and rewrite thumb file.
	 * @param details
	 * @param context
	 * @return true if all went right, false otherwise
	 * @throws FileNotFoundException 
	 */
	public static boolean removeSessionFromDisk(Details details, Context context) throws FileNotFoundException{
		// retrieve filename
		String fileName = generateFullPathName(details);
		
		if( context.deleteFile(fileName) ){
			// update thumb on the disk
			List<Details> sessions = new LinkedList<Details>();
			DataWorker.loadThumbsFromDisk(sessions, context);
			for(int i = 0; i < sessions.size(); i++){
				if(sessions.get(i).date.equals(details.date) && 
						sessions.get(i).time.equals(details.time)){
					sessions.remove(i);
				}
			}
			DataWorker.writeThumbOnDisk(sessions.iterator(), context);	
			// end update
			return true;
		}
		return false;
	}
	
	/**
	 * Write the list of recorded session on disk in XML.
	 * 
	 * @param fileName
	 *            String that represents the name of the file
	 * @param sessions
	 *            Iterator<Details> carry information about the sessions to
	 *            write 
	 * @param context
	 *            the Context of application
	 * @throws FileNotFoundException
	 *             in case the file can't be created.
	 */
	public static void writeThumbOnDisk(Iterator<Details> sessions, Context 
			context) throws FileNotFoundException
			{
		/*
		 * <?xml version='1.0' encoding='UTF-8' standalone='yes' ?>
		 * <recordings>
		 * 	<session date="21/05/11" time="15:18" duration= "360" ... X="true" Y="false" />
		 * 	<session date="21/05/11" time="16:01" ... X="true" />
		 * 	<session date="25/05/11" time="15:18" ... X="true" />
		 * </recordings>
		 */

		//create a new FileOutputStream into internal memory
		FileOutputStream fileos = context.openFileOutput(thumbName, Context.MODE_PRIVATE);

		//we create a XmlSerializer in order to write xml data
		XmlSerializer serializer = Xml.newSerializer();
		try {
			//we set the FileOutputStream as output for the serializer, using UTF-8 encoding
			serializer.setOutput(fileos, "UTF-8");
			//Write <?xml declaration with encoding (if encoding not null) and standalone flag (if standalone not null)
			serializer.startDocument(null, Boolean.valueOf(true));
			//set indentation option
			serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);

			//start a tag called "recordings"

			serializer.startTag(null, "recordings");

			Details session;
			while(sessions.hasNext()){
				serializer.startTag(null, "session");

				session = sessions.next();
				// set an attribute called "date", one called "time" 
				// and one called "axis"
				serializer.attribute(null, "date", session.date);
				serializer.attribute(null, "time", session.time);
				serializer.attribute(null, "duration", session.duration);
				serializer.attribute(null, "sample_rate", session.sample_rate);
				serializer.attribute(null, "x", String.valueOf(session.x));
				serializer.attribute(null, "y", String.valueOf(session.y));
				serializer.attribute(null, "z", String.valueOf(session.z));

				serializer.endTag(null, "session");
			}
			serializer.endTag(null, "recordings");
			serializer.endDocument();
			//write xml data into the FileOutputStream
			serializer.flush();
			//finally we close the file stream
			fileos.close();	
		} catch (Exception e) {
			Log.e
			("Exception","error occurred while creating xml file");
		}
			}

	/**
	 * 
	 * Load a list of recorded sessions from the disk in XML.
	 * 
	 * @param sessions
	 *            List<Details> the sessions are returned here
	 * @param context
	 *            the Context of application
	 */
	public static 
	void loadThumbsFromDisk(final List<Details> sessions, 
			Context context){
		final String MY_DEBUG_TAG = "WeatherForcaster";

		/*
		 * <?xml version='1.0' encoding='UTF-8' standalone='yes' ?>
		 * <recordings>
		 * 	<session date="21/05/11" time="15:18" duration= "360" sample_rate X="true" Y="false" />
		 * 	<session date="21/05/11" time="16:01" ... X="true" />
		 * 	<session date="25/05/11" time="15:18" ... X="true" />
		 * </recordings>
		 */

		try
		{
			//create a new instance of SAXParser from the Factory
			SAXParserFactory factory = SAXParserFactory.newInstance();
			SAXParser saxParser = factory.newSAXParser();

			//create a new handler to bring the data out of the XML file
			DefaultHandler handler = new 
			DefaultHandler() {

				boolean recordings = false;
				boolean session = false;

				// when a start element are found we have to bring the data out
				public void startElement(String uri, String localName, String qName, Attributes attributes)
				throws SAXException {

					if (qName.equalsIgnoreCase("recordings")) {
						recordings = true;
					}

					//if "session" we have to store data
					if (qName.equalsIgnoreCase("session")) {
						Details tmp = new Details();
						tmp.date = attributes.getValue(0);
						tmp.time = attributes.getValue(1);
						tmp.duration = attributes.getValue(2);
						tmp.sample_rate = attributes.getValue(3);
						tmp.x = Boolean.parseBoolean(attributes.getValue(4));
						tmp.y = Boolean.parseBoolean(attributes.getValue(5));
						tmp.z = Boolean.parseBoolean(attributes.getValue(6));
						
						sessions.add(tmp);
						session = true;
					}
				}

				// in case of end element, nothing to do
				public void endElement(String 
						uri, String localName,
						String qName) throws SAXException { }

				// in case of characters inside tag, nothing to do
				public void characters(char ch[], int start, int length)
				throws SAXException {

					if (recordings) {				
						recordings = false;
					}

					if 
					(session) {
						session = false;
					}
				}
			};

			//create a new FileInputStream into internal memory
			FileInputStream fileis = 
				context.openFileInput(thumbName);
			// parse xml file.
			saxParser.parse(fileis, handler);
		} catch (Exception e) {
			/* Display any Error to the GUI. */
			Log.e(MY_DEBUG_TAG, "WeatherQueryError", e);
		}
	}
}